/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.rust.cargo.impl.nodes.actions.dependencies;

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingWorker;
import org.netbeans.modules.rust.cargo.api.Cargo;
import org.netbeans.modules.rust.cargo.api.CargoTOML;
import org.netbeans.modules.rust.cargo.api.RustPackage;
import org.netbeans.modules.rust.cargo.impl.nodes.RustPackageNode;
import org.netbeans.modules.rust.cargo.impl.nodes.RustProjectDependenciesNode;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.ListView;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.ChangeSupport;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;

public final class RustAddDependencyVisualPanel1 extends JPanel implements ExplorerManager.Provider {

    private static final Logger LOG = Logger.getLogger(RustAddDependencyVisualPanel1.class.getName());

    private ExplorerManager explorerManager;
    private SwingWorker<List<RustPackage>, Void> worker;
    private final CargoTOML cargotoml;
    private final ListView resultView;
    private final List<RustPackage> packages;
    private final ChangeSupport changeSupport;
    private final RustProjectDependenciesNode.DependencyType dependencyType;

    /**
     * Creates new form RustAddDependencyVisualPanel1
     */
    public RustAddDependencyVisualPanel1(CargoTOML cargotoml, ChangeSupport changeSupport, RustProjectDependenciesNode.DependencyType dependencyType) {
        initComponents();
        this.cargotoml = cargotoml;
        this.dependencyType = dependencyType;
        this.changeSupport = changeSupport;
        this.explorerManager = new ExplorerManager();
        this.packages = new ArrayList<>();
        this.resultView = new ListView();
        resultView.setPopupAllowed(false);
        resultView.setShowParentNode(false);
        resultView.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        pnlSpacer.add(resultView, BorderLayout.CENTER);

        explorerManager.addPropertyChangeListener((event) -> handleSelectionEvent(event));

    }

    private void handleSelectionEvent(PropertyChangeEvent event) {
        if (ExplorerManager.PROP_SELECTED_NODES.equals(event.getPropertyName())) {
            Node[] nodes = explorerManager.getSelectedNodes();
            changeSupport.fireChange();
        }
    }

    @Override
    public String getName() {
        return NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "STEP_1_NAME");
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        lblSearchDependencies = new javax.swing.JLabel();
        txtCrateName = new javax.swing.JTextField();
        cmdSearch = new javax.swing.JButton();
        pnlSpacer = new javax.swing.JPanel();
        pnlFooter = new javax.swing.JPanel();
        lblStatus = new javax.swing.JLabel();
        progressSearching = new javax.swing.JProgressBar();

        setLayout(new java.awt.GridBagLayout());

        org.openide.awt.Mnemonics.setLocalizedText(lblSearchDependencies, org.openide.util.NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "RustAddDependencyVisualPanel1.lblSearchDependencies.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE;
        gridBagConstraints.insets = new java.awt.Insets(8, 4, 8, 4);
        add(lblSearchDependencies, gridBagConstraints);

        txtCrateName.setText(org.openide.util.NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "RustAddDependencyVisualPanel1.txtCrateName.text")); // NOI18N
        txtCrateName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtCrateNameActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(8, 4, 8, 4);
        add(txtCrateName, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(cmdSearch, org.openide.util.NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "RustAddDependencyVisualPanel1.cmdSearch.text")); // NOI18N
        cmdSearch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cmdSearchActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE;
        gridBagConstraints.insets = new java.awt.Insets(8, 4, 8, 4);
        add(cmdSearch, gridBagConstraints);

        pnlSpacer.setPreferredSize(new java.awt.Dimension(450, 200));
        pnlSpacer.setLayout(new java.awt.BorderLayout());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 8, 4, 8);
        add(pnlSpacer, gridBagConstraints);

        pnlFooter.setLayout(new java.awt.BorderLayout());

        org.openide.awt.Mnemonics.setLocalizedText(lblStatus, org.openide.util.NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "RustAddDependencyVisualPanel1.lblStatus.text")); // NOI18N
        pnlFooter.add(lblStatus, java.awt.BorderLayout.CENTER);
        pnlFooter.add(progressSearching, java.awt.BorderLayout.SOUTH);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 8, 4, 8);
        add(pnlFooter, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents

    private void cmdSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdSearchActionPerformed
        // Search https://crates.io using
        String text = txtCrateName.getText().trim();

        if ("".equals(text)) {
            lblStatus.setText(NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "EMPTY")); // NOI18N
            return;
        }

        if (worker != null && !worker.isDone()) {
            return;
        }

        worker = new SwingWorker<List<RustPackage>, Void>() {
            @Override
            protected List<RustPackage> doInBackground() throws Exception {
                Cargo cargo = Lookup.getDefault().lookup(Cargo.class);
                Future<List<RustPackage>> future = cargo.search(cargotoml, text);
                return future.get();
            }

            @Override
            protected void done() {
                List<RustPackage> packages;
                try {
                    packages = get();
                    setPackages(packages);
                } catch (Exception ex) {
                    Exceptions.printStackTrace(ex);
                } finally {
                    lblStatus.setText(NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "READY")); // NOI18N
                    progressSearching.setIndeterminate(false);
                }
            }
        };

        lblStatus.setText(NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "SEARCHING")); // NOI18N
        progressSearching.setIndeterminate(true);
        setPackages(Collections.emptyList());
        worker.execute();

    }//GEN-LAST:event_cmdSearchActionPerformed

    private void txtCrateNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtCrateNameActionPerformed
        cmdSearchActionPerformed(evt);
    }//GEN-LAST:event_txtCrateNameActionPerformed

    List<RustPackage> getPackages() {
        return packages;
    }

    List<RustPackage> getSelectedPackages() {
        Node[] nodes = explorerManager.getSelectedNodes();
        ArrayList<RustPackage> selected = new ArrayList<>(nodes.length);
        for (Node node : nodes) {
            RustPackage p = node.getLookup().lookup(RustPackage.class);
            if (p != null) {
                selected.add(p);
            }
        }
        return selected;
    }

    private static class SearchResultNode extends AbstractNode {

        private static class SearchResultNodeChildren extends Children.Keys<RustPackage> {

            private final List<RustPackage> crates;
            private final RustProjectDependenciesNode.DependencyType dependencyType;

            private SearchResultNodeChildren(List<RustPackage> crates, RustProjectDependenciesNode.DependencyType dependencyType) {
                this.crates = crates;
                this.dependencyType = dependencyType;
            }

            @Override
            protected void addNotify() {
                super.addNotify();
                setKeys(crates);
            }

            @Override
            protected Node[] createNodes(RustPackage key) {
                RustPackageNode n = new RustPackageNode(key, dependencyType);
                return new Node[]{n};
            }

        }

        public SearchResultNode(List<RustPackage> crates, RustProjectDependenciesNode.DependencyType dependencyType) {
            super(new SearchResultNodeChildren(crates, dependencyType), Lookup.EMPTY);
            setName(NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "SEARCH_RESULTS"));
            setDisplayName(NbBundle.getMessage(RustAddDependencyVisualPanel1.class, "SEARCH_RESULTS"));
        }

    }

    void setPackages(List<RustPackage> crates) {
        packages.clear();
        if (crates != null) {
            packages.addAll(crates);
            SearchResultNode root = new SearchResultNode(crates, dependencyType);
            explorerManager.setRootContext(root);
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cmdSearch;
    private javax.swing.JLabel lblSearchDependencies;
    private javax.swing.JLabel lblStatus;
    private javax.swing.JPanel pnlFooter;
    private javax.swing.JPanel pnlSpacer;
    private javax.swing.JProgressBar progressSearching;
    private javax.swing.JTextField txtCrateName;
    // End of variables declaration//GEN-END:variables

    @Override
    public ExplorerManager getExplorerManager() {
        return explorerManager;
    }
}
